package com.tyx.chargingpile.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.tyx.chargingpile.Enum.ServiceExceptionEume;
import com.tyx.chargingpile.Enum.ServiceMessageEume;
import com.tyx.chargingpile.cache.SocketChannelMap;
import com.tyx.chargingpile.pojo.HeartInfo;
import com.tyx.chargingpile.pojo.MonitorOrderHeartInfo;
import com.tyx.chargingpile.service.*;
import com.tyx.chargingpile.util.FastJsonUtils;
import com.tyx.chargingpile.util.Util;
import com.tyx.chargingpile.vo.HttpResult;
import com.tyx.chargingpile.vo.PileInfo;
import com.tyx.chargingpile.vo.SocketResponseResult;
import com.tyx.chargingpile.service.exception.ServiceExcep;
import com.tyx.chargingpile.util.DateUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import org.apache.http.util.TextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * 心跳处理，要验证通道
 */
@Service("chargingPileHeart")
public class ChargingPileHeart implements ChargingPileService {

    @Value("${chargingback1.url}")
    private String url;
    @Autowired
    HttpAPIService httpAPIService;
    @Autowired
    private HeartInfoService heartInfoService;
    @Autowired
    private MonitorOrderHeartInfoService monitorOrderHeartInfoService;

    private final static Logger LOGGER = LoggerFactory.getLogger(ChargingPileHeart.class);


    @Override
    public SocketResponseResult execute(ChannelHandlerContext channelHandlerContext, byte[] context) throws Exception {
        String commandId = MessageService.getCommandControl(context);
        Map<String,String> map = MessageService.analysisMessage(context, ServiceMessageEume.HEART.getUpLinePath());
        checkData(map);
        HeartInfo heartInfo = setHearInfo(map);
        if(heartInfo == null){
            new ServiceExcep(ServiceExceptionEume.HEARTERROR);
        }
        String pileNum = heartInfo.getPileNum();
        pileNum = pileNum.substring(1,pileNum.length());
        PileInfo pileInfo = SocketChannelMap.getPileInfo(pileNum);
        // 没有在缓存里面,没有注册
        if(pileInfo == null){
            LOGGER.info("客户端"+channelHandlerContext.channel().id().toString()+",指令控制编号："+commandId+",桩编号:" + pileNum + ",没有注册");
            pileInfo = getPileInfoByDeviceId(pileNum);
            if (pileInfo != null){
                // 把桩信息进行缓存，注册
                String powerType = heartInfo.getPowerType();
                pileInfo.setPowerType(powerType);
                pileInfo.setLastSurvivalTime(DateUtils.now());
                saveCache(channelHandlerContext,pileNum,pileInfo);
                updateCache(pileInfo,heartInfo);
//                LOGGER.info("桩编号"+heartInfo.getPileNum() + "在后台查询到桩信息" + pileInfo.toString() + ",重新注册成功");
                LOGGER.info("客户端"+channelHandlerContext.channel().id().toString()+",指令控制编号："+commandId+",桩编号:" + pileNum + ",重新注册成功");
            }else {
                LOGGER.info("客户端"+channelHandlerContext.channel().id().toString()+",指令控制编号："+commandId+",桩编号:" + pileNum + ",在后台查询不到桩信息，注册失败");
            }
        }else {
            updateCache(pileInfo,heartInfo);
        }
        Map<String,String> dwn = new HashMap<>();
        dwn.put("gunpointNum",heartInfo.getGunpointNum());
        dwn.put("powerType",heartInfo.getPowerType());
        dwn.put("pileNum",pileNum);
        // 请求后台桩信息，为空，返回00命令回馈
        if (pileInfo == null){
            dwn.put("commandBack","00");
        }else {
            dwn.put("commandBack","FF");
        }
        dwn.put("orderNum",heartInfo.getOrderNum());
        dwn.put("heartSerialNumber",heartInfo.getHeartSerialNumber());
        byte[] dwnContext = MessageService.getMessage(dwn,ServiceMessageEume.HEART.getDwnLinePath());
        ByteBuf pingMessage = Unpooled.buffer();
        pingMessage.writeBytes(dwnContext);
        channelHandlerContext.writeAndFlush(pingMessage);
//        LOGGER.info("客户端"+channelHandlerContext.channel().id().toString()+",指令控制编号："+commandId+",桩编号:" + pileNum + ",心跳报文下行" + Util.BCD2String(dwnContext));
        return new SocketResponseResult(channelHandlerContext,dwnContext);
    }

    @Override
    public SocketResponseResult execute(ChannelHandlerContext channelHandlerContext, Map data) throws Exception {
        return null;
    }

    /**
     * 更新缓存状态
     * @param pileInfo 充电桩信息
     * @param heartInfo 心跳报文信息
     */
    private void updateCache(PileInfo pileInfo,HeartInfo heartInfo){
        String pileTemp = heartInfo.getPileTemp();
        String gunpointNum = heartInfo.getGunpointNum();
        pileInfo.setLastSurvivalTime(DateUtils.now());
        pileInfo.setPileTemp(pileTemp);
        pileInfo.putHeartInfoMap(gunpointNum,heartInfo);
        heartInfo.setReceivedTime(DateUtils.now());
        heartInfoService.add(heartInfo);
        // TODO: 2019/4/20 保存心跳报文
        // 有心跳订单，就存入监控订单心跳表中
        // TODO: 暂时先注释，等需要时加入
//        if (!heartInfo.getOrderNum().equals("000000000000000000000000")){
//            MonitorOrderHeartInfo monitorOrderHeartInfo = setMonitorOrderHeartInfo(heartInfo);
//            monitorOrderHeartInfoService.add(monitorOrderHeartInfo);
//        }
    }

    /**
     * 报文业务校验
     * @param map
     * @throws ServiceExcep
     */
    private void checkData(Map<String,String> map) throws ServiceExcep {
        if(map == null){
            throw new ServiceExcep(ServiceExceptionEume.MESSAGEPARSINGERROR);
        }
    }

    private MonitorOrderHeartInfo setMonitorOrderHeartInfo(HeartInfo heartInfo){
        MonitorOrderHeartInfo monitorOrderHeartInfo = new MonitorOrderHeartInfo();
        monitorOrderHeartInfo.setStatus(heartInfo.getStatus());
        monitorOrderHeartInfo.setGunpointNum(heartInfo.getGunpointNum());
        monitorOrderHeartInfo.setPowerType(heartInfo.getPowerType());
        monitorOrderHeartInfo.setPileNum(heartInfo.getPileNum());
        monitorOrderHeartInfo.setOrderNum( heartInfo.getOrderNum());
        monitorOrderHeartInfo.setWarinCode( heartInfo.getWarinCode());
        monitorOrderHeartInfo.setConsumeMoney(heartInfo.getConsumeMoney());
        monitorOrderHeartInfo.setConsumeElectric(heartInfo.getConsumeElectric());
        monitorOrderHeartInfo.setSoc(heartInfo.getSoc());
        monitorOrderHeartInfo.setChargingTime(heartInfo.getChargingTime());
        monitorOrderHeartInfo.setSurplusTime(heartInfo.getSurplusTime());
        monitorOrderHeartInfo.setBatteryType(heartInfo.getBatteryType());
        monitorOrderHeartInfo.setBatteryTemp(heartInfo.getBatteryTemp());
        monitorOrderHeartInfo.setPileTemp(heartInfo.getPileTemp());
        monitorOrderHeartInfo.setGunTemp( heartInfo.getGunTemp());
        monitorOrderHeartInfo.setHighVoltage(heartInfo.getHighVoltage());
        monitorOrderHeartInfo.setMiniVoltage(heartInfo.getMiniVoltage());
        monitorOrderHeartInfo.setaVoltage(heartInfo.getaVoltage());
        monitorOrderHeartInfo.setaElectricCurrent(heartInfo.getaElectricCurrent());
        monitorOrderHeartInfo.setbVoltage(heartInfo.getbVoltage());
        monitorOrderHeartInfo.setbElectricCurrent(heartInfo.getbElectricCurrent());
        monitorOrderHeartInfo.setcVoltage(heartInfo.getcVoltage());
        monitorOrderHeartInfo.setcElectricCurrent(heartInfo.getcElectricCurrent());
        monitorOrderHeartInfo.setReserve(heartInfo.getReserve());
        monitorOrderHeartInfo.setHeartSerialNumber(heartInfo.getHeartSerialNumber());
        monitorOrderHeartInfo.setReceivedTime(heartInfo.getReceivedTime());
        return monitorOrderHeartInfo;
    }

    private HeartInfo setHearInfo(Map<String,String> map){
        HeartInfo heartInfo = new HeartInfo();
        heartInfo.setStatus(map.get("status"));
        heartInfo.setGunpointNum(map.get("gunpointNum"));
        heartInfo.setPowerType(map.get("powerType"));
        heartInfo.setPileNum(map.get("pileNum"));
        heartInfo.setOrderNum(map.get("orderNum"));
        heartInfo.setWarinCode(map.get("warinCode"));
        heartInfo.setConsumeMoney(map.get("consumeMoney"));
        heartInfo.setConsumeElectric(map.get("consumeElectric"));
        heartInfo.setSoc(map.get("soc"));
        heartInfo.setChargingTime(map.get("chargingTime"));
        heartInfo.setSurplusTime(map.get("surplusTime"));
        heartInfo.setBatteryType(map.get("batteryType"));
        heartInfo.setBatteryTemp(map.get("batteryTemp"));
        heartInfo.setPileTemp(map.get("pileTemp"));
        heartInfo.setGunTemp(map.get("gunTemp"));
        heartInfo.setHighVoltage(map.get("highVoltage"));
        heartInfo.setMiniVoltage(map.get("miniVoltage"));
        heartInfo.setaVoltage(map.get("aVoltage"));
        heartInfo.setaElectricCurrent(map.get("aElectricCurrent"));
        heartInfo.setbVoltage(map.get("bVoltage"));
        heartInfo.setbElectricCurrent(map.get("bElectricCurrent"));
        heartInfo.setcVoltage(map.get("cVoltage"));
        heartInfo.setcElectricCurrent(map.get("cElectricCurrent"));
        heartInfo.setReserve(map.get("reserve"));
        heartInfo.setHeartSerialNumber(map.get("heartSerialNumber"));
        return heartInfo;
    }

    /**
     * 请求后台，获取电桩信息
     * @param device_id
     * @return
     * @throws Exception
     */
    private PileInfo getPileInfoByDeviceId(String device_id) throws Exception {
        Map<String,Object> parMap = new HashMap<>();
        parMap.put("act","getPile");
        parMap.put("device_id",device_id);
        HttpResult result = httpAPIService.doPost(url,parMap);
        if(result.getCode() != 200){
            return null;
        }
        Map map = FastJsonUtils.stringToCollect(result.getBody());
        if(StringUtils.isEmpty(map.get("code")) || !"200".equals(map.get("code"))){
            return null;
        }
        if(StringUtils.isEmpty(map.get("data"))){
            return null;
        }
        JSONObject jsonObject = (JSONObject) map.get("data");
        PileInfo info = FastJsonUtils.toBean(jsonObject.toJSONString(),PileInfo.class);
        return info;
    }

    /**
     * 保存缓存
     * @param channelHandlerContext 通道上细纹
     * @param pileNum 电桩号码
     * @param pileInfo 电桩信息
     */
    private void saveCache(ChannelHandlerContext channelHandlerContext,String pileNum,PileInfo pileInfo){
        SocketChannelMap.addChannelMap(channelHandlerContext.channel().id().toString(),pileNum,channelHandlerContext,pileInfo);
    }
}
